package com.example.lgseckill.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.lgseckill.commom.Constant;
import com.example.lgseckill.domain.SeckillProduct;
import com.example.lgseckill.domain.SeckillProductPreheatVO;
import com.example.lgseckill.mapper.SeckillProductMapper;
import com.example.lgseckill.service.ISeckillProducttService;
import com.example.lgseckill.commom.utils.RedisUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Author:e
 * Date:2021/8/16 5:12 下午
 **/
@Service
public class SeckillProductServiceImpl
        extends ServiceImpl<SeckillProductMapper, SeckillProduct>
        implements ISeckillProducttService {

    private final static Logger logger = LoggerFactory.getLogger(SeckillProductServiceImpl.class);


    @Autowired
    private SeckillProductMapper seckillProductMapper;

    @Autowired
    private RedisUtil redisUtil;


    /**
     * 定时扫描大于2分钟后开始的商品加入缓存
     */
    @Scheduled(fixedRate = 60000)
    public void scheduled() {
        this.seckillProductPreheat();
    }


    /**
     * 秒杀商品预热到redis 中
     *
     * @return
     */
    @Override
    public void seckillProductPreheat() {
        Date now = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String gtNowStr = formatter.format(now) + ":00";
        int status = 0;
        Date leNow = DateUtils.addMinutes(now, 2);
        String leNowStr = formatter.format(leNow) + ":00";
        List<SeckillProductPreheatVO> productPreheatVoS =
                seckillProductMapper.seckillProductPreheat(status, gtNowStr, leNowStr);
        logger.info("2分钟后{} - {}开始秒杀的商品有{}个：{}", gtNowStr, leNowStr, productPreheatVoS.size(),
                productPreheatVoS);
        //待更新的状态
        List<SeckillProduct> seckillProductList = new ArrayList<>();

        //将秒杀商品id 存入redis list 中
        //list 令牌桶
        List<Long> tokenList = new ArrayList();
        productPreheatVoS.forEach(seckillProductPreheatVO -> {
            //开始时间
            long startTime = seckillProductPreheatVO.getStartTime().getTime();
            //结束时间
            long endTime = seckillProductPreheatVO.getEndTime().getTime();
            //计算离结束还有多久时间（作为过期时间）
            long expire = (endTime - now.getTime()) / 1000;
            //秒杀持续时间（ms）
            long duration = endTime - startTime;

            //秒杀数量
            int number = seckillProductPreheatVO.getNumber();
            for (int i = 0; i < number; i++) {
                //将秒杀商品id放入list
                tokenList.add(seckillProductPreheatVO.getSeckillProductId());
            }
            //秒杀id 与商品的映射
            redisUtil.set(
                    Constant.SECKILL_PRODUCT_INFO + seckillProductPreheatVO.getSeckillProductId(),
                    seckillProductPreheatVO,
                    expire);

            //存入redis 队列
            Collections.sort(tokenList);
            logger.info("秒杀令牌：{}", tokenList);

            //从右压入队列 防止超卖，当值获取完后，在获取就为空代表抢完
            redisUtil.rightPushAll(
                    Constant.SECKILL_TOKEM + seckillProductPreheatVO.getSeckillProductId(),
                    tokenList);
            //设置过期时间
            redisUtil.expire(Constant.SECKILL_TOKEM +
                    seckillProductPreheatVO.getSeckillProductId(), expire);

            //更新为已经预热
            SeckillProduct seckillProduct = new SeckillProduct();
            seckillProduct.setSeckillProductId(seckillProductPreheatVO.getSeckillProductId());
            seckillProduct.setStatus(1);
            seckillProductList.add(seckillProduct);
        });
        if (!seckillProductList.isEmpty()){
            //更新状态
                    seckillProductMapper.updateBatchBySecckillId(seckillProductList);
            logger.info("Redis 预热了{}条数据", seckillProductList.size());
        }
    }

    @Override
    public List<SeckillProductPreheatVO> getSeckillList() {
        return seckillProductMapper.getSeckillList();
    }

    @Override
    public void productRefresh(Long seckillId) {
        seckillProductMapper.productRefresh(seckillId);
    }
}
